package com.mvc.framework.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.mvc.framework.annotation.MyController;
import com.mvc.framework.annotation.MyRequestMapping;
import com.mvc.framework.annotation.MyRequestParam;
import com.mvc.framework.context.MyApplicationContext;
import com.mvc.framework.handler.Handler;
import com.mvc.framework.handler.HandlerAdapter;
import com.mvc.framework.resolver.ViewResolver;

/**
 * Servlet implementation class MyDispatcherServlet
 */
public class MyDispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private static final String LOCATION = "myContextConfigLocation";
	private List<Handler> handlerMapping = new ArrayList<Handler>();
	private Map<Handler, HandlerAdapter> adapterMapping = new HashMap<Handler, HandlerAdapter>();
	private List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();

	public MyDispatcherServlet() {
	}

	// init
	public void init(ServletConfig config) throws ServletException {

		MyApplicationContext context = new MyApplicationContext(config.getInitParameter(LOCATION));
		// 请求解析
		initMultipartResolver(context);
		// 多语言、国际化
		initLocaleResolver(context);
		// 主题View层的
		initThemeResolver(context);

		// ============== 重要 ================
		// 解析url和Method的关联关系
		initHandlerMappings(context);
		// 适配器（匹配的过程）
		initHandlerAdapters(context);
		// ============== 重要 ================

		// 异常解析
		initHandlerExceptionResolvers(context);
		// 视图转发（根据视图名字匹配到一个具体模板）
		initRequestToViewNameTranslator(context);

		// ============== 重要 ================
		// 解析模板中的内容（拿到服务器传过来的数据，生成HTML代码）
		initViewResolvers(context);
		// ============== 重要 ================

		initFlashMapManager(context);

		System.out.println("MySpring MVC is init.");
	}

	private void initViewResolvers(MyApplicationContext context) {
		String tempateRoot = context.getConfig().getProperty("templateRoot");
		String rootPath = this.getClass().getClassLoader().getResource(tempateRoot).getFile();

		File rootDir = new File(rootPath);
		File[] listFiles = rootDir.listFiles();
		for (File template : listFiles) {
			System.out.println("template.getName()===========" + template.getName());
			viewResolvers.add(new ViewResolver(template.getName(), template));
		}
	}

	// 适配器（匹配的过程）
	// 主要是用来动态匹配我们参数的
	private void initHandlerAdapters(MyApplicationContext context) {

		if (handlerMapping.isEmpty()) {
			return;
		}

		Map<String, Integer> paramMapping = new HashMap<String, Integer>();
		for (Handler handler : handlerMapping) {

			// 这里是匹配HttpServletRequest和HttpServletResponse
			Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
			for (int i = 0; i < parameterTypes.length; i++) {
				Class<?> parameterType = parameterTypes[i];
				if (parameterType == HttpServletRequest.class || parameterType == HttpServletResponse.class) {
					System.out.println("parameterType.getName()==========" + parameterType.getName());
					paramMapping.put(parameterType.getName(), i);
				}
			}

			// 第一个维度对应参数列表里参数，
			// 第二个维度对应一个参数对应的多个注解
			Annotation[][] parameterAnnotations = handler.getMethod().getParameterAnnotations();
			for (int i = 0; i < parameterAnnotations.length; i++) {
				for (Annotation annotation : parameterAnnotations[i]) {
					if (annotation instanceof MyRequestParam) {
						String paramvalue = ((MyRequestParam) annotation).value().trim();
						if (!"".equals(paramvalue)) {
							paramMapping.put(paramvalue, i);
						}
					}
				}
			}

			adapterMapping.put(handler, new HandlerAdapter(paramMapping));
		}
	}

	// 解析url和Method的关联关系
	private void initHandlerMappings(MyApplicationContext context) {
		Map<String, Object> ioc = context.getInstanceMapping();

		if (ioc.isEmpty()) {
			return;
		}

		// 扫描ioc容器中找到所有Controller类
		Set<Entry<String, Object>> entrySet = ioc.entrySet();
		for (Entry<String, Object> entry : entrySet) {
			Class<? extends Object> clazz = entry.getValue().getClass();
			// 不是controller忽略
			if (!clazz.isAnnotationPresent(MyController.class)) {
				continue;
			}

			String url = "";
			if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
				MyRequestMapping myRequestMappingannotation = clazz.getAnnotation(MyRequestMapping.class);
				String value = myRequestMappingannotation.value();
				if (!"".equals(value)) {
					url = value;
				}
			}

			// 处理此controller下面的所有方法
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (!method.isAnnotationPresent(MyRequestMapping.class)) {
					continue;
				}
				MyRequestMapping myRequestMappingAnnotation = method.getAnnotation(MyRequestMapping.class);
				String regex = (url + myRequestMappingAnnotation.value()).replaceAll("/+", "/");
				System.out.println("regex=======" + regex);
				Pattern pattern = Pattern.compile(regex);
				System.out.println("pattern========" + pattern);
				handlerMapping.add(new Handler(pattern, entry.getValue(), method));
			}

			System.out.println("handlerMapping=========" + handlerMapping);
		}
	}

	private void initThemeResolver(MyApplicationContext context) {
		// TODO Auto-generated method stub

	}

	private void initLocaleResolver(MyApplicationContext context) {
		// TODO Auto-generated method stub

	}

	private void initFlashMapManager(MyApplicationContext context) {
		// TODO Auto-generated method stub

	}

	private void initRequestToViewNameTranslator(MyApplicationContext context) {
		// TODO Auto-generated method stub

	}

	private void initHandlerExceptionResolvers(MyApplicationContext context) {
		// TODO Auto-generated method stub

	}

	private void initMultipartResolver(MyApplicationContext context) {

	}

	// doGet
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}

	// doPost
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			doDispatch(request, response);
		} catch (Exception e) {
			response.getWriter().write("500 Exception, Msg :" + Arrays.toString(e.getStackTrace()));
		}
	}

	private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 先取出来一个Handler，从HandlerMapping取
		Handler handler = getHandler(request);
		if (handler == null) {
			response.getWriter().write("404 Not Found");
			return;
		}

		// 再取出来一个适配器
		// 再由适配去调用我们具体的方法
		HandlerAdapter ha = getHandlerAdapter(handler);
		MyModelAndView mv = ha.handle(request, response, handler);
		System.out.println("mv==============" + mv);

		applyDefaultViewName(response, mv);
	}

	private void applyDefaultViewName(HttpServletResponse response, MyModelAndView mv) throws Exception {
		if (mv == null) {
			return;
		}

		if (viewResolvers.isEmpty()) {
			return;
		}

		for (ViewResolver resolver : viewResolvers) {
			if (!mv.getView().equals(resolver.getViewName())) {
				continue;
			}
			String result = resolver.parse(mv);
			if (result != null) {
				response.getWriter().write(result);
				break;
			}
		}
	}

	private HandlerAdapter getHandlerAdapter(Handler handler) {
		if (adapterMapping.isEmpty()) {
			return null;
		}
		return adapterMapping.get(handler);
	}

	private Handler getHandler(HttpServletRequest request) {
		if (handlerMapping.isEmpty()) {
			return null;
		}

		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		System.out.println("requestURI========" + request.getRequestURI());
		System.out.println("request.getContextPath()=======" + request.getContextPath());

		url = url.replace(contextPath, "").replaceAll("/+", "/");
		for (Handler handler : handlerMapping) {
			Matcher matcher = handler.getPattern().matcher(url);
			if (!matcher.matches()) {
				continue;
			}
			return handler;
		}
		return null;
	}

}
